package com.csx.governance.traffic.adapter.outbound.webclient;


import cn.hutool.core.collection.CollectionUtil;
import com.csx.governance.traffic.adapter.config.TrafficLoadBalancerConfiguration;
import com.csx.governance.traffic.adapter.outbound.LoadBalancerInstanceSupplierBeanPostProcessor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientSpecification;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.web.reactive.function.client.ClientRequest;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author cuisongxu
 * @date 2023/4/13 9:49
 */
@Aspect
public class TrafficReactorLoadBalancerAspect {

    private final GenericApplicationContext applicationContext;

    public TrafficReactorLoadBalancerAspect(GenericApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Pointcut(value = "execution(* org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerExchangeFilterFunction.filter(..))")
    public void point(){}

    @Around(value = "point()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{

        Object[] args = joinPoint.getArgs();
        ClientRequest clientRequest = (ClientRequest) args[0];
        String serviceName = clientRequest.url().getHost();

        registerOutBoundServiceNameBean(serviceName);

        addConfigurationsToLoadBalancerClientFactory();

        return joinPoint.proceed();
    }


    /**
     * <p>注册serviceName名称的Bean,类型为 {@link LoadBalancerClientSpecification},主要作用为向LoadBalancer的子容器中注册 {@link TrafficLoadBalancerConfiguration}</p>
     * <p>该类是一个配置类，配置类中注册了一个后置处理器 {@link LoadBalancerInstanceSupplierBeanPostProcessor},负责包装 LoadBalancer子容器中的 {@link ServiceInstanceListSupplier}类型的Bean</p>
     * <p>使得所有 {@link ServiceInstanceListSupplier} 中加入全链路灰度自定义的实例过滤逻辑</p
     */
    private void registerOutBoundServiceNameBean(String serviceName) {
        if(!applicationContext.containsBean(serviceName)) {
            applicationContext.registerBean(serviceName, LoadBalancerClientSpecification.class, () -> new LoadBalancerClientSpecification(serviceName, new Class[]{TrafficLoadBalancerConfiguration.class}));
        }
    }

    /**
     * <p>将容器中所有的 {@link LoadBalancerClientSpecification} 类型的Bean加入到 {@link LoadBalancerClientFactory#setConfigurations(List)}中, 使得在创建LoadBalancer子容器时能够加入
     * {@link LoadBalancerClientSpecification} 中自定义的配置类(这里是加入负责包装{@link ServiceInstanceListSupplier}的后置处理器,添加全链路灰度的实例过滤逻辑)</p>
     */
    private void addConfigurationsToLoadBalancerClientFactory() {
        LoadBalancerClientFactory loadBalancerClientFactory = applicationContext.getBean(LoadBalancerClientFactory.class);

        Map<String, LoadBalancerClientSpecification> loadBalancerClientSpecificationMap = applicationContext.getBeansOfType(LoadBalancerClientSpecification.class);
        List<LoadBalancerClientSpecification> loadBalancerClientSpecificationList = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(loadBalancerClientSpecificationMap)) {
            loadBalancerClientSpecificationList = new ArrayList<>(loadBalancerClientSpecificationMap.values());
        }

        loadBalancerClientFactory.setConfigurations(loadBalancerClientSpecificationList);
    }
}
